# VideoMetadataSearch - Search video file metadata from IMDB database
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
import time
import imdb
import string
import urllib
import threading
from pysqlite2 import dbapi2 as sqlite

class VideoMetadataSearch(threading.Thread):
    """Search video file metadata from IMDB database."""
    
    # Video cache database
    __VIDEO_DB = os.path.expanduser('~/.entertainer/cache/video.db')
    
    # Video cover art directory
    __ART_DIRECTORY = os.path.expanduser('~/.entertainer/cache/movie_art')
    
    # Title of the movie / TV-series episode
    __TITLE = None
    
    # Season number (if TV-series)
    __SEASON = None
    
    # Episode number (if TV-series)
    __EPISODE = None
    
    # We search metadata for this file
    __FILENAME = None
    
    # IMDB interface
    __IMDB = None
    
    # Logger object
    __logger = None
    
    def __init__(self, filename, logger):
        """Initialize metadata search thread."""
        threading.Thread.__init__(self)
        self.setName("Video metadata search thread")
        self.__logger = logger
        self.__FILENAME = filename
        self.__TITLE, self.__SEASON, self.__EPISODE = self.__parseFilename(filename)
        try:
            self.__IMDB = imdb.IMDb()
        except IMDbError:
            raise IOError("Couldn't connect to IMDB server!")

    def __parseFilename(self, filename):
        """
        Parse filename. Tries to get title of the movie/tv-series etc.
        
        TV-episode filenames should be format:
            tv-series-2-13-Prison Break.avi, where 2 is season and 13 is episode
        
        Movies should be something like: 
            movie.name-xvid[something][something].avi
            movie.name.avi
            movie_name[Something].avi
            etc.
        """
        #FIXME: This method could be done better with regexp.
        body = filename[filename.rfind('/')+1 : filename.rfind('.')]
        if body[:10] == "tv-series-":
            full = body[10:]
            season = full[:full.find('-')]
            temp = full[full.find('-')+1:]
            episode = temp[:temp.find('-')]
            title = temp[temp.find('-')+1:]
        else:
            if body.find('[') != -1:
                temp = body[:body.find('[')]
            else:
                temp = body
            if temp.find('-') != -1:
                title = temp[:temp.find('-')]
            else:
                title = temp
            season = 0
            episode = 0
            
        try:
            int(season)
            int(episode)
        except:
            raise Exception("Invalid filename format, can't search metadata!")
        
        title = title.replace('_', ' ').replace('.', ' ').strip().title()
        return title, int(season), int(episode)
    
    def run(self):
        """Search metadata from IMDB and update video cache database."""
        search_results = []
        try:
            search_results = self.__IMDB.search_movie(self.__TITLE)
        except:
            self.__logger.error("IMDB search failed")
            return # Network error or too many results to handle
            
        if len(search_results) == 0:
            return # No matches for this search
        
        if search_results[0]['kind'] == "movie":
            movie = search_results[0] # We trust that the first search result is the best
            try:
                self.__IMDB.update(movie)
            except:
                self.__logger.error("Metadata search failed for: " + self.__FILENAME)
                return
                
            video_type = "MOVIE"
            try:
                title = movie['title']
                year = movie['year']
                rating = round(float(movie['rating']) / 2) # convert to 5-stars rating
                genres = string.join(movie['genres'],",")
                plot_outline = movie['plot outline']
                plot_string = movie['plot'][0]
                plot = plot_string[plot_string.rfind("::")+2:].lstrip()
                
                # IMDb returns sometimes list and sometimes string
                if(type(plot)) is list:
                    plot = plot[0]   
                if(type(plot_outline)) is list:
                    plot_outline = plot_outline[0]
                
                try:
                    runtime = movie['runtime'][0]
                    int(runtime) # Raises exception if not integer
                except:
                    runtime = 0
                p = self.__getPersons(movie)
                row = (video_type,title,"",runtime,genres,rating,
                       year,plot_outline,plot,0,0,p[0],p[1],p[2],
                       p[3],p[4],p[5],p[6],p[7],p[8], self.__FILENAME)
                self.__updateVideoCache(row)
                
                # Download and save cover art
                try:
                    self.__downloadCoverArt(movie['cover url'], title)
                except:
                    pass
            except:
                self.__logger.error("Couldn't find metadata from IMDB for: " + self.__FILENAME)
                return
            
        elif search_results[0]['kind'] == "tv series":
            series = search_results[0] # We trust that the first search result is the best
            try:
                self.__IMDB.update(series)
                self.__IMDB.update(series, "episodes")
            except:
                self.__logger.error("Couldn't find metadata from IMDB for: " + self.__FILENAME)
                return
                
            video_type = "TV-SERIES"
            p = self.__getPersons(series)
            rating = 0 #round(float(series['rating']) / 2) # convert to 5-stars rating
            series_title = series['title']
            genres = string.join(series['genres'],",")
            try:
                time = series['runtime']
                runtime = time[0][:time[0].find(":")]
                int(runtime) # This raises exception if runtime is not integer
            except:
                runtime = 0
            try:
                year = series['series years']
            except:
                year = series['year']
            try:
                title = series['episodes'][self.__SEASON][self.__EPISODE]['title']
            except:
                title = str(self.__EPISODE) + ". Episode"
            try:
                plot =  series['episodes'][self.__SEASON][self.__EPISODE]['plot']
            except:
                plot = "No plot outline available"
            row = (video_type,title,series_title,runtime,genres,rating,year,"",
                   plot,self.__SEASON,self.__EPISODE,p[0],p[1],p[2],p[3],p[4],
                   p[5],p[6],p[7],p[8], self.__FILENAME)
            self.__updateVideoCache(row)
            
            # Download and save cover art
            try:
                self.__downloadCoverArt(series['cover url'], series_title)
            except:
                pass
        else:
            # This file wasn't identified to be a movie or a TV-series episode.
            return
                   
    def __updateVideoCache(self, db_row):
        """Update data to video cache database"""
        #print "Update metadata for " + db_row[1] #FOR DEBUGGING
        try:
            db_conn = sqlite.connect(self.__VIDEO_DB)
            db_cursor = db_conn.cursor()
            db_cursor.execute("""UPDATE metadata 
                                 SET type=?,
                                     title=?,
                                     series_title=?,
                                     runtime=?,
                                     genres=?,
                                     rating=?,
                                     year=?,
                                     plot_outline=?,
                                     plot=?,
                                     season=?,
                                     episode=?,
                                     actor_1=?,
                                     actor_2=?,
                                     actor_3=?,
                                     actor_4=?,
                                     actor_5=?,
                                     writer_1=?,
                                     writer_2=?,
                                     director_1=?,
                                     director_2=?
                                 WHERE filename=?""", db_row)
            db_conn.commit()
            db_conn.close()
        except Exception, e:
            self.__logger.error("Exception occured during video cache update: " + e.message)
            

    def __getPersons(self, movie):
        """
        Returns a list of persons.
        
        First five names are actors, then comes two direcors and two writers.
        """
        try:       
            a1 = movie['actors'][0]['name']
        except:
            a1 = ""
        try:     
            a2 = movie['actors'][1]['name']
        except:
            a2 = ""
        try: 
            a3 = movie['actors'][2]['name']
        except:
            a3 = ""
        try: 
            a4 = movie['actors'][3]['name']
        except:
            a4 = ""
        try: 
            a5 = movie['actors'][4]['name']
        except:
            a5 = ""
        try: 
            w1 = movie['writer'][0]['name']
        except:
            w1 = ""
        try: 
            w2 = movie['writer'][1]['name']
        except:
            w2 = ""
        try: 
            d1 = movie['director'][0]['name']
        except:
            d1 = ""
        try: 
            d2 = movie['director'][1]['name']
        except:
            d2 = ""

        return [a1,a2,a3,a4,a5,w1,w2,d1,d2]
        
    def __downloadCoverArt(self, url, title):
        """Download cover art from given URL and save it to cache"""
        if not os.path.exists(os.path.join(self.__ART_DIRECTORY, title + ".jpg")):
            try:
                image = urllib.urlopen(url)
            except:
                return
            dest = open(os.path.join(self.__ART_DIRECTORY, title + ".jpg"),'w')
            dest.write(image.read())
            dest.close()
